package com.lmm.rpc.core.server;

import com.lmm.rpc.core.codec.DefaultDecoder;
import com.lmm.rpc.core.codec.DefaultEncoder;
import com.lmm.rpc.core.handler.DefaultRequestHandler;
import com.lmm.rpc.core.register.ZkRegister;
import com.lmm.rpc.core.transport.MessageHandler;
import com.lmm.rpc.core.util.IPUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * netty服务端
 * */
@Slf4j
public class NettyServer implements Server{
    //业务线程池
    private final ExecutorService threadPoolExecutor;
    //业务处理器
    private final MessageHandler messageHandler;
    private final EventLoopGroup bossGroup;
    private final EventLoopGroup workerGroup;
    private final ServerBootstrap serverBootstrap = new ServerBootstrap ();
    private int stop =0;
    private static final int shutDownWaitTime = 3;
    /**
     * 注册中心
     * */
//    private final ZkRegister zkRegister;

    public NettyServer(ExecutorService threadPoolExecutor,MessageHandler messageHandler,int bossThread,int workThread){
        this.threadPoolExecutor = threadPoolExecutor;
        this.messageHandler = messageHandler;
        if (Epoll.isAvailable()){
            this.bossGroup = new EpollEventLoopGroup(bossThread);
            this.workerGroup = new EpollEventLoopGroup(workThread);
        }else {
            this.bossGroup = new NioEventLoopGroup(bossThread);
            this.workerGroup = new NioEventLoopGroup(workThread);
        }
    }
    @Override
    public void start(int port) {
        try{
            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(workerGroup instanceof EpollEventLoopGroup ? EpollServerSocketChannel.class: NioServerSocketChannel.class)
//                    .handler(new LoggingHandler(LogLevel.INFO))
                    .option ( ChannelOption.SO_BACKLOG, 1024 )
                    .option ( ChannelOption.SO_REUSEADDR, true )
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline()
                                    .addLast(new DefaultDecoder())
                                    .addLast(new DefaultEncoder<>())
                                    .addLast(new DefaultRequestHandler(threadPoolExecutor, messageHandler));
                        }
                    });
            serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true);
            serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
            ChannelFuture bind = serverBootstrap.bind(port);
            bind.syncUninterruptibly();
            log.info("netty server ip:{} start success ! port:{}", IPUtil.getIpV4(),port);

        }catch (Exception e){
            log.error ( "nettyServer start fail !",e );
            if(bossGroup != null) bossGroup.shutdownGracefully ();
            if(workerGroup != null) workerGroup.shutdownGracefully ();
        }

    }

    @Override
    public void stop() {
        if (stop == 0){
            stop = 1;
            log.info("ready to stopping server !");
            log.info ( "wait for netty service over " + shutDownWaitTime * 1000 + "ms" );
            try {
                Thread.sleep ( shutDownWaitTime * 1000 );
            } catch (Exception e) {
            }
            if(threadPoolExecutor!=null){
                threadPoolExecutor.shutdown ();
            }
            if(bossGroup != null) bossGroup.shutdownGracefully ();
            if(workerGroup != null) workerGroup.shutdownGracefully ();
            log.info("server stopped success");
            stop = 2;
        }
        while (stop == 1){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                log.error("netty server stop waiting ");
            }
        }
    }
}
